/*
 * ServerSocket.cpp
 *
 *  Created on: 07.10.2012
 *      Author: Sven
 */

// TODO: Delete obsolete sessions

#include "../header/Main.h"

DWORD CServerSocket::run(CServerSocket* socket)
{
	while(true)
	{
		Sleep(1);

		//
		socket->recvs();
		//

		//
		CPacket* pack = CPacket::pull();
		if(pack)
		{
			pack->handle();
			delete pack;
		}
		//

		// TODO: Per session handling
		// iterate through map
		//

		//
		socket->sends();
		//
	}

	return 0;
}

int CServerSocket::init(int port)
{
	#if PLATFORM == PLATFORM_WINDOWS
		WSADATA WsaData;
	    if(WSAStartup( MAKEWORD(2,2), &WsaData ) != NO_ERROR)
	    	return 1;
	#endif

	m_handle = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
	if(m_handle <= 0)
		return 2;

	m_addr.sin_family = AF_INET;
	m_addr.sin_addr.s_addr = INADDR_ANY;
	m_addr.sin_port = htons((unsigned short)port);

	if(bind(m_handle, (const sockaddr*)&m_addr, sizeof(sockaddr_in)) < 0)
		return 3;

	#if PLATFORM == PLATFORM_MAC || PLATFORM == PLATFORM_UNIX
		int nonBlocking = 1;
		if(fcntl(handle, F_SETFL, O_NONBLOCK, nonBlocking) == -1)
			return 4;
	#else
		DWORD nonBlocking = 1;
		if(ioctlsocket(m_handle, FIONBIO, &nonBlocking) != 0)
			return 4;
	#endif

	return 0;
}

void CServerSocket::close()
{
	#if PLATFORM == PLATFORM_MAC || PLATFORM == PLATFORM_UNIX
		close(m_handle);
	#elif PLATFORM == PLATFORM_WINDOWS
		closesocket(m_handle);
		WSACleanup();
	#endif
}

void CServerSocket::recvs()
{
	if(m_handle == 0)
		return;

	char buffer[1024];
	int fromLength = sizeof(sockaddr_in);
	sockaddr_in addr;

	int bytes = recvfrom(m_handle, buffer, sizeof(buffer), 0, (sockaddr*)&addr, &fromLength);

	if(bytes > 2)
	{
		BASE_PACKET* packet = (BASE_PACKET*)buffer;
		if(packet->size == bytes)
		{
			CSession* session = CSession::m_sessions[addr.sin_addr.s_addr];
			if(packet->type == PACKET_AUTH)
			{
				if(session)
				{
					delete session;
				}

				if(packet->size < 7)
					return;

				// String checks
				int strsize = packet->size - 3;
				char* str1 = (char*)packet->data;
				int str1size = strlen(str1);
				char* str2 = (char*)str1+str1size+1;
				int str2size = strlen(str2);

				if(strsize < (str1size + str2size + 2)) // +2 because of zero-terminated strings
					return;

				// Lookup player
				// TODO: Authentication
				CPlayer* player = new CPlayer(str1);
				cout << "Authenticated: " << str1 << endl;

				// Setup session
				session = new CSession();
				session->init(&addr);
				session->setplayer(player);
				CSession::m_sessions[addr.sin_addr.s_addr] = session;

				//cout << "Sessions: " << CSession::m_sessions.size() << endl;

				// Answer
				BASE_PACKET* newpacket = (BASE_PACKET*)new char[4];
				newpacket->size		= 4;
				newpacket->type		= PACKET_AUTH;
				newpacket->data[0]	= 1;
				CPacket::m_sendqueue.push(new CPacket(newpacket, session));
			}
			else if(session)
			{
				BASE_PACKET* newpacket = (BASE_PACKET*)new char[packet->size];
				memcpy(newpacket, packet, packet->size);
				CPacket::m_rcvqueue.push(new CPacket(newpacket, session));
			}
		}
	}
}

void CServerSocket::sends()
{
	if(m_handle == 0)
		return;

	if(CPacket::m_sendqueue.empty())
		return;

	CPacket* packet = CPacket::m_sendqueue.front();
	CPacket::m_sendqueue.pop();

	BASE_PACKET* base = packet->getpacket();

	CSession* session = packet->getsession();

	if(session)
	{
		sendto(m_handle, (char*)base, base->size, 0, (sockaddr*)session->getaddr(), sizeof(sockaddr_in));
		//cout << "send: " << base->size << endl;
	}
	else
	{
		map<unsigned int, CSession*>::iterator it;
		for(it = CSession::m_sessions.begin(); it != CSession::m_sessions.end(); it++)
		{
			session = it->second;
			sendto(m_handle, (char*)base, base->size, 0, (sockaddr*)session->getaddr(), sizeof(sockaddr_in));
			//cout << "send: " << base->size << endl;
		}
	}

	delete packet;
}
